package pikasial.fs;

import java.io.File;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;

import pikasial.images.PikImage;

/**
 * Représentation d'un dossier dans le système de fichier de Pikasial.
 * <p>
 * Cette classe représente un dossier ajouté par l'utilisateur dans le
 * système de fichier de Pikasial. Un dossier est un conteneur d'images,
 * et possède une collection d'images (celle-ci est remplie à chaque fois
 * que l'on scanne le dossier)
 * Il possède également des informations complémentaires.
 *
 * @author ESIAL - Groupe 14
 * @see pikasial.images.ImageContainer
 * @see pikasial.fs.FSElement
 */
public class Dossier extends FSElement
    implements pikasial.images.ImageContainer, Comparable<Dossier> {

    /** Collection des images contenues dans le dossier. */
    protected List<PikImage> images = null;
    /** Collection des sous-dossiers. */
    protected SortedSet<Dossier> sousDossiers = null;


    /**
     * Construit une instance de dossier à partir du chemin abstrait f.
     * <p>
     * Un dossier dans Pikasial correspond à un dossier présent sur le disque,
     * il s'agit d'une organisation physique. Par contre, le contenu du dossier
     * dans Pikasial peut être différent du contenu du même dossier sur le
     * disque (ex: si certaines images seulement ont été importées, où si un
     * des sous-dossiers sur le disque ne contient pas d'image).
     *
     * @param f - chemin abstrait du dossier.
     * @see java.io.File
     */
    public Dossier(final File f) {
        super(f);
        images = Collections.synchronizedList(
            new ArrayList<PikImage>());
        sousDossiers = Collections.synchronizedSortedSet(
            new TreeSet<Dossier>());
    }

    /**
     * Ajoute toutes les images de la collection spécifiée à la liste
     * d'images contenues dans ce dossier.
     * <p>
     * Les images sont ajoutées une par une, pour pouvoir tester
     * leur éventuelle présence dans la collection.
     *
     * @param collecImages - la collection d'images à ajouter
     * @see pikasial.images.ImageContainer#addAllImages(java.util.Collection)
     */
    public void addAllImages(final Collection<PikImage> collecImages) {
        if (!collecImages.isEmpty()) {
            images.addAll(collecImages);
        }
    }

    /**
     * Ajoute l'image spécifiée à la collection d'images contenues
     * dans ce dossier
     * <p>
     * La méthode teste si l'image n'est pas déjà présente
     * dans la collection avant de l'ajouter.
     *
     * @param image - l'image à ajouter
     * @see pikasial.images.ImageContainer#addImage(pikasial.images.PikImage)
     */
    public void addImage(final PikImage image) {
        if (image != null && !images.contains(image)) {
            images.add(image);
        }
    }

    /**
     * Teste si ce dossier contient un fichier d'image ou plus, sur le disque.
     * <p>
     * La méthode teste si le dossier contient des images, sur le disque local,
     * et non pas dans Pikasial. Il s'agit d'un scan non récursif.
     *
     * @return true si le dossier contient au moins une image, faux sinon.
     */
    boolean containsImageOnDisk() {
        File[] fich = super.file.listFiles();
        for (File element : fich) {
            if (element.isFile() && (new Fichier(element)).containsImage()) {
                return true;
            }
        }
        return false;
    }

    /**
     * Teste si ce dossier contient au moins un sous-dossier
     * contenant (récursivement) des images.
     * <p>
     * Cette vérification a lieu sur le disque local. Il s'agit d'un scan
     * récursif.
     *
     * @return vrai si le dossier contient au moins une image dans l'un
     * de ses sous-dossiers (même un lointain descendant)
     */
    private boolean containsImageOnDiskRecursive() {
        File[] fich = super.file.listFiles();
        for (File element : fich) {
            if (element.isDirectory()) {
                if ((new Dossier(element)).containsImageOnDiskRecursive()) {
                    return true;
                }
            }
        }
        return containsImageOnDisk();
    }

    /**
     * Retourne la collection complète des images contenues dans ce dossier
     * dans Pikasial.
     * <p>
     * Equivalent à <code>getImageRange(0,getImageCount())</code>
     *
     * @return les images contenues dans ce dossier.
     */
    public Collection<PikImage> getAllImages() {
        return images;
    }

    /**
     * Retourne l'image contenue à l'indice spécifié.
     *
     * @param index - l'indice de l'image
     * @return l'image contenue à l'indice donné en paramètre,
     * null si l'indice est erronné
     * @see pikasial.images.ImageContainer#getImage(int)
     */
    public PikImage getImage(final int index) {
        return images.get(index);
    }

    /**
     * Retourne le nombre d'images contenues dans ce dossier.
     *
     * @return le nombre d'images dans ce dossier
     * @see pikasial.images.ImageContainer#getImageCount()
     */
    public int getImageCount() {
        return images.size();
    }

    /**
     * Retourne la collection d'image comprise dans l'intervalle
     * spécifié.
     *
     * @param indA le premier indice de l'intervalle (inclus)
     * @param indB le dernier indice de l'intervalle (exclus)
     * @return la collection d'images dans l'intervalle, ou null
     * si les indices sont incorrects
     *
     * @see pikasial.images.ImageContainer#getImageRange(int, int)
     */
    public Collection<PikImage> getImageRange(final int indA, final int indB) {
        if (indA > indB) {
            throw new IllegalArgumentException(
                "Le premier paramètre indA doit " +
                "être inférieur au deuxième !");
        }
        Collection<PikImage> collec = new ArrayList<PikImage>();
        int i = 0;
        boolean dansInterv = false;
        for (PikImage pik : images) {
            if (i == indA) {
                dansInterv = true;
            }
            if (dansInterv) {
                collec.add(pik);
            }
            if (i++ == indB) {
                break;
            }
        }
        return collec;
    }

    /**
     * Pour accèder à la "liste" ordonnée des sous-dossiers
     * contenant des images.
     *
     * @return les sous-dossiers
     */
    public SortedSet<Dossier> getSousDossiers() {
        return sousDossiers;
    }

    /**
     * Retourne la dernière date à laquelle le dossier a été modifié.
     *
     * @return la date de dernière modification
     */
    public Date getDateModifiction() {
        return new Date(file.lastModified());
    }

    /**
     * Teste si ce dossier contient des sous-dossiers contenant des images.
     *
     * @return vrai si le dossier contient des sous-dossiers
     */
    public boolean hasSousDossiers() {
        return !sousDossiers.isEmpty();
    }

    /**
     * Redéfinit la méthode de la classe mère:
     * teste si l'élement est un dossier.
     *
     * @return vrai, c'est un dossier
     * @see pikasial.fs.FSElement#isDirectory()
     */
    @Override
    public boolean isDirectory() {
        return true;
    }

    /**
     * Supprime toutes les images du dossier, dans Pikasial.
     *
     * @see pikasial.images.ImageContainer#removeAll()
     */
    public void removeAll() {
        images.clear();
    }

    /**
     * Supprime l'image contenue à l'indice spécifié.
     *
     * @param index - l'indice de l'image à supprimer
     * @see pikasial.images.ImageContainer#removeImage(int)
     */
    public void removeImage(final int index) {
    	images.remove(index);
    }

	public void removeImage(PikImage pik) {
		images.remove(pik);
	}

    /**
     * Scanne le dossier sur le disque local.
     * <p>
     * Remplit la collection d'images, et la liste des sous-dossiers
     * qu'il contient.
     * Un sous-dossier n'est ajouté que s'il contient récursivement au moins une
     * image.
     * @return - la liste des élements contenant des images.
     */
    public void scanFolderOnDisk() {
        // Récupère la liste des fichiers sur le disque
        File[] fich = super.file.listFiles();
        for (File element : fich) {
            if (element.isDirectory()) {
                Dossier d = new Dossier(element);
                if (d.containsImageOnDiskRecursive()) {
                    sousDossiers.add(d);
                }
            } else if (element.isFile()) {
                Fichier f = new Fichier(element);
                if (f.containsImage()) {
                    addImage(new PikImage(element));
                }
            }
        }
    }

    /**
     * Teste si le dossier contient des images dans Pikasial.
     * @return vrai si le dossier contient des images
     */
    @Override
    public boolean containsImage() {
        return !images.isEmpty();
    }

    public boolean isEmpty() {
        return images.isEmpty();
    }

    public void removeRange(int ind1, int ind2) {
        if (ind1 > ind2) {
            throw new IllegalArgumentException(
                "Le premier paramètre ind1 doit " +
                "être inférieur au deuxième !");
        }
        Iterator<PikImage> iter = images.iterator();
        boolean dansInterv = false;
        int i = 0;
        while (iter.hasNext()) {
            iter.next();
            if (i == ind1) {
                dansInterv = true;
            }
            if (dansInterv) {
                iter.remove();
            }
            if (i++ == ind2) {
                break;
            }
        }
    }

    public int compareTo(Dossier o) {
        return file.getPath().compareTo(o.getPath());
    }

	public boolean containsImage(PikImage pik) {
		return images.contains(pik);
	}

	public boolean containsImage(String name) {
		boolean trouve = false;
		for (PikImage pik : images) {
			if (pik.getName().equals(name)) {
				trouve = true;
				break;
			}
		}
		return trouve;
	}

}
